(function()
{
    var window = this,
        t = document.createTextNode('default xml namespace = window._defaultNS;'),
        h = document.getElementsByTagName('head')[0],
        dns = [], last, undefined;
    
    /**
     *    Use this function to ensure cross-browser compatible
     *    default namespace declaration
     *
     *    @param Namespace | String ns
     */
    XML.setDefaultNamespace = function (ns)
    {
        var a = window._defaultNS,
            s = document.createElement('script');
        dns.unshift(last || "");
        s.type = 'text/javascript;e4x=1';
        s.appendChild(t);
        last = window._defaultNS = ns.prefix
                            ? new Namespace(ns.uri, ns.prefix)
                            : new Namespace(ns.uri || ns + "");
        h.removeChild(h.appendChild(s));
        window._defaultNS = a;
    };
    
    /**
     *  Use this function to restore the default namespace 
     *  to the previous namespace
     *
     */
    XML.restoreDefaultNamespace = function ()
    {
        var a = window._defaultNS,
            s = document.createElement('script');
        s.type = 'text/javascript;e4x=1';
        s.appendChild(t);
        last = window._defaultNS = dns.shift() || "";
        h.removeChild(h.appendChild(s));
        window._defaultNS = a;
    };
    
    /**
     *  
     *
     *
     */
    XML.load = function (pathToFile, onload)
    {
        var xhr = new XMLHttpRequest(),
            async = ({}).toString.call(onload || {}) == "[object Function]";
        
        xhr.open('GET', pathToFile, async);
        
        if (async)
        {
            xhr.addEventListener("load", function(){onload(new XML((xhr.responseText||"").replace(/\s*<\?xml.*?\?>/,'')))}, false);
        }
        
        xhr.send(null);
        
        return async ? xhr : new XML((xhr.responseText||"").replace(/\s*<\?xml.*?\?>/,''));
    };
    
    /**
     *
     *
     *
     */
    XML.prototype.function::Delete = function (PropertyName)
    {
        delete this[PropertyName];
        return null;
    };
    
    /**
     *
     *
     *
     */
    XML.prototype.function::Put = function (PropertyName, Value)
    {
        this[PropertyName] = Value;
        return null;
    };
    
    /**
     *
     *
     *
     */
    XMLList.prototype.function::Append = function (Value)
    {
        var v = XMLList(Value instanceof XML ? Value : XML(Value)), 
            i = 0, l = v.length();
        
        while (i < l)
        {
            this[this.length()] = v[i++];
        }
        return null;
    };
    
    /**
     *
     *
     *
     */
    XML.prototype.function::Equals = function (Value)
    {
        return this == Value;
    };

    
    /*
     * e4x.js
     * 
     * A JavaScript library that implements the optional E4X features described in
     * ECMA-357 2nd Edition Annex A if they are not already implemented.
     *
     * 2010-03-13
     * 
     * By Elijah Grey, http://eligrey.com
     * License: The X11/MIT license (see COPYING.md)
     *
     * Changes: 
     *    By Sam Shull, http://samshull.blogspot.com
     *    Just a litlle simplifying
     */
    
    /*global document, XML, XMLList, DOMParser, XMLSerializer, XPathResult */
    
    /*jslint undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true,
      newcap: true, immed: true, maxerr: 1000, maxlen: 90 */
    
    var xmlMediaType  = "application/xml",
        domParser     = new DOMParser,
        xmlSerializer = new XMLSerializer,
        xmlDoc         = domParser.parseFromString("<x/>", xmlMediaType),
        piName         = /^[\w\-]+\s*/;
    
        /**
         *
         *
         *
         */
        function createDocumentFrom (xml)
        {
            // XXX: Figure out a way to create a document without DOMParser or implent an
            //      XPath engine for E4X (which would have to be another whole library and be
            //      slower than the native DOM's engine).
            
            return xml instanceof XMLList
                    ? domParser.parseFromString(
                        xml.toXMLString(), 
                        xmlMediaType
                    )
                    : domParser.parseFromString(
                        '<x>' + xml.toXMLString() + '</x>', 
                        xmlMediaType
                    );
        }
        
        /**
         *
         *
         *
         */
        function xmlToDomNode (xml)
        {
            switch (xml.nodeKind())
            {
                case "element":
                    return createDocumentFrom(xml).documentElement;
                
                case "text":
                    return xmlDoc.CreateTextNode(xml.toString());
                
                case "comment":
                    return xmlDoc.CreateComment(xml.toString().slice(4, -3));
                
                case "processing-instruction":
                    return xmlDoc.CreateProcessingInstruction(
                        xml.localName(),
                        xml.toString().slice(2, -2).replace(piName, "")
                    );
                
                case "attribute":
                    var node;
                    (node = xmlDoc.createAttributeNS(
                        xml.namespace().uri,
                        xml.localName()
                    )).nodeValue = xml.toString();
                    return node;
            }
        }
        
        /**
         *
         *
         *
         */
        XML.prototype.function::domNode = function ()
        {
            return this.length() == 1 && document.adoptNode(xmlToDomNode(this));
        };
        
        /**
         *
         *
         *
         */
        XMLList.prototype.function::domNodeList = function ()
        {
            return this.length() > 0 && document.adoptNode(createDocumentFrom(this).documentElement).childNodes;
        };
        
        /**
         *
         *
         *
         */
        XML.prototype.function::xpath = function (xpathExp)
        {
            var res = new XMLList, 
                i, l = this.length(),
                domDoc, xpr, node;
    
            if (l !== 1) {
                for (i = 0; i < l; ++i) {
                    res += this[i].xpath(xpathExp);
                }
                return res;
            }
            
            domDoc = createDocumentFrom(this);
            xpr = domDoc.evaluate(
                xpathExp,
                domDoc,
                domDoc.createNSResolver(domDoc),
                XPathResult.ORDERED_NODE_ITERATOR_TYPE,
                null
            );
    
            while (node = xpr.iterateNext()) {
                res += new XML(xmlSerializer.serializeToString(node));
            }
    
            return res;
        };
        
        /**
         *
         *
         *
         */
        XML.prototype.function::transform = function (xslt, params)
        {
            if (!xslt instanceof XML)
            {
                throw new TypeError();
            }
            
            var res, i, l = this.length(), xsltProcessor;
            
            if (l > 1)
            {
                res = new XMLList();
                for (i = 0; i < l; ++i)
                {
                    res.Append(this[i].transform(xslt));
                }
                return res;
            }
            
            xsltProcessor = new XSLTProcessor();
            
            xsltProcessor.importStyleSheet(createDocumentFrom(xslt));
            
            for (i = 0, l = (params||[]).length; i < l; ++i)
            {
                res = params[i];
                xsltProcessor.setParameter(res.namespaceURI, res.localName, res.value);
            }
            
            res = xsltProcessor.transformToDocument(createDocumentFrom(this));
                
            return new XML(xmlSerializer.serializeToString(res.documentElement));
        };

})();


